package com.rsstuan.crawl.impl;

import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.appengine.api.images.ImagesServiceFactory;
import com.rsstuan.parser.HtmlParser;
import com.rsstuan.parser.RssParser;
import com.rsstuan.service.ChannelService;
import com.rsstuan.service.ImageService;
import com.rsstuan.storage.data.TuanGouChannelData;
import com.rsstuan.storage.data.TuanGouItemData;
import com.rsstuan.storage.impl.JDOImageStorage;
import com.rsstuan.storage.impl.JDOTuanGouChannelStorage;
import com.rsstuan.storage.impl.JDOTuanGouItemStorage;
import com.rsstuan.utils.ChannelUtils;
import com.rsstuan.utils.ImageUtils;
import com.rsstuan.utils.StringUtils;
import com.rsstuan.utils.TimeUtils;

public class CrawlerService {
	
	private final static Logger LOG = Logger.getLogger(CrawlerService.class.getName());
	
	private JDOTuanGouChannelStorage channelStorage;
	
	private JDOTuanGouItemStorage itemStorage;

	private HtmlCrawler crawler;
	
	private static ImageService imageService = ImageService.getInstance();
	
	public CrawlerService() {
		channelStorage = new JDOTuanGouChannelStorage();
		itemStorage = new JDOTuanGouItemStorage();
		crawler = new HtmlCrawler();
	}
	public void doCrawl() {
		//load
		LOG.info("enter doCrawl");
		TuanGouChannelData[] channels = loadChannels();
		if (channels == null)
			return;
		ChannelUtils.sortByChannelId(channels);
		
		long lastCrawlChannelId = ChannelService.getInstance().getLastCrawlChannelId();
		//long lastCrawlChannelId = -1L;
		LOG.info("doCrawl, channel size=" + channels.length + " lastChannelId=" + lastCrawlChannelId);
		int size = 5;
		int crawled = 0;
		int i = 0;
		while (crawled  < size && i < channels.length) {
			TuanGouChannelData channel = channels[i++];
			try {
				if (channel.getId() <= lastCrawlChannelId)
					continue;
				lastCrawlChannelId = channel.getId();
				ChannelService.getInstance().setLastCrawlChannelId(lastCrawlChannelId);
				doCrawlOne(channel);
				crawled ++;
				LOG.info("crawl channel=" + channel.getTitle() + " lastCrawlChannelId=" + lastCrawlChannelId);
			}
			catch (Exception e) {
				LOG.log(Level.WARNING, "fail to crawl item for channel = " + channel.getTitle(), e);
			}
		}
		i = 0;
		while (crawled < size && i < channels.length) {
			TuanGouChannelData channel = channels[i++];
			try {
				lastCrawlChannelId = channel.getId();
				ChannelService.getInstance().setLastCrawlChannelId(lastCrawlChannelId);
				doCrawlOne(channel);
				crawled ++;
				LOG.info("crawl channel=" + channel.getTitle() + " lastCrawlChannelId=" + lastCrawlChannelId);
			}
			catch (Exception e) {
				LOG.log(Level.WARNING, "fail to crawl item for channel = " + channel.getTitle(), e);
			}
		}	
		LOG.info("crawl " + crawled + " channels");
	}
	
	public void doUpdate() {
		List<TuanGouChannelData> channels = loadActiveChannels(10);
		for (TuanGouChannelData channel : channels) {
			try {
			     doUpdateOne(channel);
			}
			catch (Exception e) {
				LOG.log(Level.WARNING, "fail to crawl item for channel = " + channel.getTitle(), e);
			}
		}
	}
	
	public void doCrawlOne(TuanGouChannelData channel) {
	    
        TuanGouItemData hItem = doCrawlHtml(channel);
        if (hItem == null)
        	return;
        LOG.info("crawl html Item, title =" + hItem.getTitle());
        // filter
       List<TuanGouItemData> allItems = itemStorage.getItemsByChannel(channel.getId());
       List<TuanGouItemData> eItems = new ArrayList<TuanGouItemData>();
       for (TuanGouItemData item : allItems) {
    	   if (item.isActivate())
    		   eItems.add(item);
       }
       TuanGouItemData aItem = null;
       //查找是否有相同的商品
       if (eItems != null && eItems.size() > 0) {
            for (TuanGouItemData eItem : eItems) {
            	if (isSameItem(eItem, hItem)) {
            		aItem = eItem;
            		LOG.info("find equal items");
            		break;
            	}
            }
        }
       //若存在相同的商品，则更新
        if (aItem != null) {
			if (hItem.getOrderNum() > aItem.getOrderNum()) {
				itemStorage.updateOrderNum(aItem.getId(), hItem.getOrderNum());
				LOG.info("update orderNum for item=" + aItem.getTitle());
			}
			if (!StringUtils.isBlank(hItem.getImage()) 
					&& (StringUtils.isBlank(aItem.getImage()) 
							|| !hItem.getImage().equals(aItem.getImage()))) {
				itemStorage.updateImageUrl(aItem.getId(), hItem.getImage());
			}
			
			/*if (StringUtils.isBlank(aItem.getImage()) && !StringUtils.isBlank(hItem.getImage())) {
				crawlImage(aItem.getId(), hItem.getImage());
			}*/
			
			for (TuanGouItemData eItem : eItems) {
				if (eItem != aItem) {
					itemStorage.deactiveItem(eItem.getId());
				}
			}
			return;
		}
        //若不存在，则准备插入
        TuanGouItemData cItem = fillItem(hItem, channel);
        if (cItem != null) {
            
            itemStorage.saveItem(cItem);
            /*if (cItem.getImage() != null) {
                crawlImage(cItem.getId(), cItem.getImage());
            }*/
            if(eItems != null) {
	           //将所有其他活跃的商品设置为不活跃
            	for (TuanGouItemData eItem : eItems) {
	            	itemStorage.deactiveItem(eItem.getId());
	            }
            }
            LOG.info("save new item="+cItem.getTitle());
        } else {
            LOG.info("No new item for channel=" + channel.getTitle());
        }
	}
	
	private boolean isSameItem(TuanGouItemData li, TuanGouItemData ri) {
		if (li.getTitle() == null || ri.getTitle() == null )
			return true;
		if (li.getTitle().equals(ri.getTitle()))
				return true;
		return false;
	}
	
	public TuanGouItemData fillItem(TuanGouItemData item, TuanGouChannelData channel) {
		long now = System.currentTimeMillis();
		Date d = new Date(now);
		if (item == null)
			return null;
		item.setUpdateTime(now);
		item.setStoreTime(now);
		if (item.getPubTime() == null) {
			item.setPubTime(now);
		}
		if (item.getStartTime() == null) {
			item.setStartTime(now);
		}
		
		item.setActivate(true);
		//TODO 暂时认为期限为一天
		if (item.getEndTime() == null ) {
		    item.setEndTime(now + TimeUtils.DAY_MILLISECONDS);
		} else {
		    if (item.getLeftTime() != null) {
		        item.setStartTime(item.getEndTime() - item.getLeftTime());
		    } else {
		        item.setStartTime(item.getEndTime() - TimeUtils.DAY_MILLISECONDS);
		    }
		}
		
		//set info about channel
		item.setChannelId(channel.getId());
        item.setCity(channel.getCity());
        item.setSiteId(channel.getSiteId());
        if (item.getLink() == null) {
            item.setLink(channel.getLink());
        }
		return item;
	}
	
	private TuanGouItemData combineItems(TuanGouItemData htmlItem, TuanGouItemData rssItem) {
		if (htmlItem == null) 
			return rssItem;
		if (rssItem == null) 
			return htmlItem;
		TuanGouItemData cItem = new TuanGouItemData();
		if (StringUtils.isBlank(rssItem.getTitle()))
			cItem.setTitle(rssItem.getTitle());
		else
			cItem.setTitle(htmlItem.getTitle());
		
		if (StringUtils.isBlank(rssItem.getLink()))
			cItem.setLink(rssItem.getLink());
		else
			cItem.setLink(htmlItem.getLink());
		
		if (StringUtils.isBlank(rssItem.getImage())) 
			cItem.setImage(rssItem.getImage());
		else
			cItem.setImage(rssItem.getImage());
		if (StringUtils.isBlank(rssItem.getDescription())) 
			cItem.setDescription(rssItem.getDescription());
		else
			cItem.setDescription(rssItem.getDescription());
		
		if (htmlItem.getPrice() != null )
			cItem.setPrice(htmlItem.getPrice());
		else
			cItem.setPrice(rssItem.getPrice());
		
		if (htmlItem.getOrigPrice() != null )
			cItem.setOrigPrice(htmlItem.getOrigPrice());
		else
			cItem.setOrigPrice(rssItem.getOrigPrice());
		
		cItem.setAddress(htmlItem.getAddress());
		cItem.setOrderNum(htmlItem.getOrderNum());
		cItem.setEndTime(htmlItem.getEndTime());
		cItem.setLeftTime(htmlItem.getLeftTime());
		
		return cItem;
	}
 	private TuanGouChannelData[] loadChannels() {
		//List<TuanGouChannelData> channels = channelStorage.getInAcitveChannels();
		List<TuanGouChannelData> channels = ChannelService.getInstance().getAllChannels();
		if (channels != null) 
			return channels.toArray(new TuanGouChannelData[0]);
		else
			return null;
	}
 	
	private List<TuanGouChannelData> loadActiveChannels(int size) {
		List<TuanGouChannelData> channels = channelStorage.getAcitveChannels();
		
		return channels;
	}
	
	public TuanGouItemData doCrawlHtml(TuanGouChannelData channel){
		try {
			String html = crawler.crawl(channel.getLink());
			HtmlParser parser = new HtmlParser(html, channel.getLink());
			TuanGouItemData item = parser.parseToTuanGouItem();
			
			return item;
		}
		catch (Exception e) {
			return null;
		}
	}
	
	private List<TuanGouItemData> doCrawlRss(TuanGouChannelData channel) {
		try {
			String rss = crawler.crawl(channel.getLink());
			List<TuanGouItemData> items = RssParser.parseToTuanGouItems(rss, channel.getLink());
			
			for (TuanGouItemData item : items) {
				item.setChannelId(channel.getId());
				item.setCity(channel.getCity());
			}
			return items;
		}
		catch (Exception e)  {
			LOG.log(Level.WARNING, "doCrawlRss fail with channel = " + channel.getTitle(), e);
			return null;
		}
	}

	
	
	public void doUpdateOne(TuanGouChannelData channel) {
	    
        TuanGouItemData hItem = doCrawlHtml(channel);
        LOG.info("crawl html Item, title =" + hItem.getTitle());
        
       // List<TuanGouItemData> rItems = doCrawlRss(channel);
        List<TuanGouItemData> rItems = null;
        // filter
        List<TuanGouItemData> eItems = itemStorage.getActivateItemsByChannel(channel.getId());
        if (eItems != null) {
            for (TuanGouItemData eItem : eItems) {
                if (isSameItem(eItem, hItem)) {
                    if (eItem.getOrderNum() < hItem.getOrderNum()) {
                        itemStorage.updateOrderNum(eItem.getId(), hItem.getOrderNum());
                    	channelStorage.updateLastUpdateTime(eItem.getChannelId());
                    }
                }
            }
        }
	}
	
	public void crawlImage(Long itemId, String imageUrl) {
	    try {
	        byte[] data = crawler.crawlImage(imageUrl);
	        byte[] thumbData = ImageUtils.makeThumbnail(data);
	        
	        imageService.saveImage(itemId, thumbData);
	    }
	    catch (Exception e) {
	        LOG.log(Level.WARNING, "SAVE Image failed, url=" + imageUrl, e);
	    }
	}
}
